#if defined(WIN32) || defined(WIN64)
#include "windows.h"
#include "io.h"
#else
#include "unistd.h"
#endif

#include <stdlib.h>
#include <openssl/ossl_typ.h>
#include <openssl/hmac.h>
#include <unistd.h>
#include <math.h>
#include "MQTTAsync.h"
#include "string.h"
#include "string_util.h"


char *uri = "63c33b9647.st1.iotda-device.cn-south-1.myhuaweicloud.com";
int port = 1883;
extern char *username = "68956056a72147363d968474_myNodeId"; 
extern char *password = "12345678";

int gQOS = 1;  //default value of qos is 1
int keepAliveInterval = 120; //default value of keepAliveInterval is 120s
int connectTimeout = 30; //default value of connect timeout is 30s
int retryInterval = 10; //default value of connect retryInterval is 10s
char *ca_path = "./conf/rootcert.pem";
MQTTAsync client = NULL;
int flag = 0; //0: deviceId access; 1:nodeId access, just for old mqtt api.

#define TRY_MAX_TIME 				100   //Maximum length of attempted encryption
#define SHA256_ENCRYPTION_LENGRH 	32
#define TIME_STAMP_LENGTH 			10
#define PASSWORD_ENCRYPT_LENGTH 	64

int mqttClientCreateFlag = 0; //this mqttClientCreateFlag is used to control the invocation of MQTTAsync_create, otherwise, there would be message leak.
int retryTimes = 0;
int minBackoff = 1000;
int maxBackoff = 30*1000; //10 seconds
int defaultBackoff = 1000;

void mqtt_connect_success(void *context, MQTTAsync_successData *response) {
	retryTimes = 0;
	printf("connect success. \n");
}

void TimeSleep(int ms) {
#if defined(WIN32) || defined(WIN64)
	Sleep(ms);
#else
    usleep(ms * 1000);
#endif
}

void mqtt_connect_failure(void *context, MQTTAsync_failureData *response) {
	retryTimes++;
	printf("connect failed: messageId %d, code %d, message %s\n", response->token, response->code, response->message);
	//�˱�����
	int lowBound =  defaultBackoff * 0.8;
	int highBound = defaultBackoff * 1.2;
	int randomBackOff = rand() % (highBound - lowBound + 1);
	long backOffWithJitter = (int)(pow(2.0, (double)retryTimes) - 1) * (randomBackOff + lowBound);
	long waitTImeUntilNextRetry = (int)(minBackoff + backOffWithJitter) > maxBackoff ? maxBackoff : (minBackoff + backOffWithJitter);

	TimeSleep(waitTImeUntilNextRetry);

	//connect
	int ret = mqtt_connect();
	if (ret != 0) {
		printf("connect failed, result %d\n", ret);
	}
}

int EncryWithHMacSha256(const char *inputData, char **inputKey, int inEncryDataLen, char *outData) {

	if (inputData == NULL || (*inputKey) == NULL) {
		printf("encryWithHMacSha256(): the input is invalid.\n");
		return -1;
	}

	if (TIME_STAMP_LENGTH != strlen(*inputKey)) {
		printf("encryWithHMacSha256(): the length of inputKey is invalid.\n");
		return -1;
	}

	char *end = NULL;
	unsigned int mac_length = 0;
	unsigned int tryTime = 1;
	int lenData = strlen(inputData);
	long timeTmp = strtol(*inputKey, &end, 10);
	unsigned char *temp = HMAC(EVP_sha256(), *inputKey, TIME_STAMP_LENGTH, (const unsigned char*) inputData, lenData, NULL, &mac_length);

	while (strlen(temp) != SHA256_ENCRYPTION_LENGRH) {
		tryTime++;
		if (tryTime > TRY_MAX_TIME) {
			printf("encryWithHMacSha256(): Encryption failed after max times attempts.\n");
			return -1;
		}

		timeTmp++;
		snprintf(*inputKey, TIME_STAMP_LENGTH + 1, "%ld", timeTmp);
		temp = HMAC(EVP_sha256(), *inputKey, TIME_STAMP_LENGTH, (const unsigned char*) inputData, lenData, NULL, &mac_length);
	}

	int uiIndex, uiLoop;
	char ucHex;

	for (uiIndex = 0, uiLoop = 0; uiLoop < inEncryDataLen; uiLoop++) {
		ucHex = (temp[uiLoop] >> 4) & 0x0F;
		outData[uiIndex++] = (ucHex <= 9) ? (ucHex + '0') : (ucHex + 'a' - 10);

		ucHex = temp[uiLoop] & 0x0F;
		outData[uiIndex++] = (ucHex <= 9) ? (ucHex + '0') : (ucHex + 'a' - 10);
	}

	outData[uiIndex] = '\0';

	return 0;
}

int GetEncryptedPassword(char **timestamp, char **encryptedPwd) {
	if (password == NULL) {
		return -1;
	}

	char *temp_encrypted_pwd = NULL;
	string_malloc(&temp_encrypted_pwd, PASSWORD_ENCRYPT_LENGTH + 1);
	if (temp_encrypted_pwd == NULL) {
		printf("GetEncryptedPassword() error, there is not enough memory here.\n");
		return -1;
	}

	int ret = EncryWithHMacSha256(password, timestamp, SHA256_ENCRYPTION_LENGRH, temp_encrypted_pwd);
	if (ret != 0) {
		printf( "GetEncryptedPassword() error, encrypt failed %d\n", ret);
		free(temp_encrypted_pwd);
		temp_encrypted_pwd = NULL;
		return -1;
	}

	if (CopyStrValue(encryptedPwd, (const char*) temp_encrypted_pwd, PASSWORD_ENCRYPT_LENGTH) < 0) {
		printf("GetEncryptedPassword(): there is not enough memory here.\n");
		free(temp_encrypted_pwd);
		temp_encrypted_pwd = NULL;
		return -1;
	}

	free(temp_encrypted_pwd);
	temp_encrypted_pwd = NULL;

	return 0;
}



//receive message from the server
// 引用同级目录的cJSON库
#include "cJSON.h"
#include <stdio.h>
#include <string.h>
#include <pthread.h>
// 引入温度模块头文件以获取温度和互斥锁
#include "../../TEMPERATURE/temperature.h"
// 根据实际路径调整LED控制头文件引用
#include "../../LED/led_control.h"

// 外部声明温度模块中的互斥锁和变量（与temperature.c对应）
extern pthread_mutex_t control_mutex;
extern int manual_led_override;
extern float current_temperature;
extern lv_obj_t *ui_ledSwitch;

// 声明设置开关状态的函数（来自temperature.c）
extern void set_switch_state(lv_obj_t *sw, int state);

// 接收服务器消息处理函数
int mqtt_message_arrive(void *context, char *topicName, int topicLen, MQTTAsync_message *message) {
    if (message->payload == NULL) {
        printf("mqtt_message_arrive() 收到空的消息内容\n");
        return 1;
    }

    // 转换payload为字符串
    char *payload = (char *)message->payload;
    printf("mqtt_message_arrive() 成功接收消息，主题: %s，内容: %s \n", topicName, payload);

    // 解析JSON
    cJSON *root = cJSON_Parse(payload);
    if (root == NULL) {
        printf("JSON解析失败: %s\n", cJSON_GetErrorPtr());
        return 1;
    }

    // 检查是否为ui_ledSwitch命令
    cJSON *cmd_name = cJSON_GetObjectItem(root, "command_name");
    if (cmd_name && cmd_name->type == cJSON_String && 
        strcmp(cmd_name->valuestring, "ui_ledSwitch") == 0) {
        
        // 验证service_id是否匹配
        cJSON *service_id = cJSON_GetObjectItem(root, "service_id");
        if (service_id && service_id->type == cJSON_String &&
            strcmp(service_id->valuestring, "smokeDetector") == 0) {
            
            // 提取参数中的value值
            cJSON *paras = cJSON_GetObjectItem(root, "paras");
            if (paras && paras->type == cJSON_Object) {
                cJSON *value = cJSON_GetObjectItem(paras, "value");
                if (value && value->type == cJSON_Number) {
                    int led_state = value->valueint;
                    
                    // 仅处理0和1两种状态
                    if (led_state == 0 || led_state == 1) {
                        // 加锁确保线程安全（与temperature.c同步）
                        pthread_mutex_lock(&control_mutex);
                        
                        // 关键：标记为手动控制模式，优先级高于自动控制
                        // 允许在0℃-100℃全范围生效
                        if (current_temperature >= 0 && current_temperature <= 100.0f) {
                            manual_led_override = 1;  // 接管控制权
                            set_led_state(led_state); // 控制硬件
                            set_switch_state(ui_ledSwitch, led_state); // 同步UI
                            printf("云平台控制LED状态为 %d（0=关闭，1=开启）- 已接管控制权\n", led_state);
                        } else {
                            printf("温度超出范围(%.1f℃)，无法通过云平台控制LED\n", current_temperature);
                        }
                        
                        pthread_mutex_unlock(&control_mutex);
                    } else {
                        printf("无效的LED状态: %d（仅允许0或1）\n", led_state);
                    }
                } else {
                    printf("参数中缺少或无效的'value'字段\n");
                }
            } else {
                printf("缺少或无效的'paras'字段\n");
            }
        } else {
            printf("服务ID不匹配（预期为'smokeDetector'）\n");
        }
    } else {
        printf("忽略非ui_ledSwitch命令\n");
    }

    // 释放cJSON资源
    cJSON_Delete(root);
    return 1;
}
    
    


    







MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
MQTTAsync_SSLOptions ssl_opts = MQTTAsync_SSLOptions_initializer;

void mqtt_connection_lost(void *context, char *cause) {
	printf("mqtt_connection_lost() error, cause: %s\n", cause);
	//������������������
}

int mqtt_connect() {

	char *encrypted_password = NULL;

	if (!mqttClientCreateFlag) {
		char *temp_authMode = "_0_0_";
		
		if (flag == 1) {
			temp_authMode = "_2_0_";
		}
		
		conn_opts.cleansession = 1;
		conn_opts.keepAliveInterval = keepAliveInterval;
		conn_opts.connectTimeout = connectTimeout;
		conn_opts.retryInterval = retryInterval;
		conn_opts.onSuccess = mqtt_connect_success;
		conn_opts.onFailure = mqtt_connect_failure;

		char *loginTimestamp = get_client_timestamp();
		if (loginTimestamp == NULL) {
			return -1;
		}

		int encryptedRet = GetEncryptedPassword(&loginTimestamp, &encrypted_password);
		if (encryptedRet != 0) {
			free(loginTimestamp);
			loginTimestamp = NULL;
			return -1;
		}

		if(port == 8883) {
			if (access(ca_path, 0)) {
				printf("ca file is NOT accessible\n");
				free(loginTimestamp);
				loginTimestamp = NULL;
				return -1;
			}
			ssl_opts.trustStore = ca_path;
			ssl_opts.enabledCipherSuites = "TLSv1.2";
			ssl_opts.enableServerCertAuth = 1; // 1: enable server certificate authentication, 0: disable
			// ssl_opts.verify = 0; // 0 for no verifying the hostname, 1 for verifying the hostname
			conn_opts.ssl = &ssl_opts;
		}

		char *clientId = NULL;
		clientId = combine_strings(3, username, temp_authMode, loginTimestamp);
		free(loginTimestamp);
		loginTimestamp = NULL;

		int createRet = MQTTAsync_create(&client, uri, clientId, MQTTCLIENT_PERSISTENCE_NONE, NULL);
		free(clientId);
		clientId = NULL;

		if (createRet) {
			printf("mqtt_connect() MQTTAsync_create error, result %d\n", createRet);
		} else {
			mqttClientCreateFlag = 1;
			printf("mqtt_connect() mqttClientCreateFlag = 1.\n");
		}

		MQTTAsync_setCallbacks(client, NULL, mqtt_connection_lost, mqtt_message_arrive, NULL);

	}

	conn_opts.username = username;
	conn_opts.password = encrypted_password;

	printf("begin to connect the server.\n");
	int ret = MQTTAsync_connect(client, &conn_opts);
	if (ret) {
		printf("mqtt_connect() error, result %d\n", ret);
		return -1;
	}

	if (encrypted_password != NULL) {
		free(encrypted_password);
		encrypted_password = NULL;
	}

	return 0;

}

void publish_success(void *context, MQTTAsync_successData *response) {
	printf("publish success, the messageId is %d \n", response ? response->token : -1);
}

void publish_failure(void *context, MQTTAsync_failureData *response) {
	printf("publish failure\n");
	if(response) {
		printf("publish_failure(), messageId is %d, code is %d, message is %s\n", response->token, response->code, response->message);
	}
}

int mqtt_publish(const char *topic, char *payload) {

	MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
	MQTTAsync_message pubmsg = MQTTAsync_message_initializer;

	opts.onSuccess = publish_success;
	opts.onFailure = publish_failure;

	pubmsg.payload = payload;
	pubmsg.payloadlen = (int) strlen(payload);
	pubmsg.qos = gQOS;
	pubmsg.retained = 0;

	int ret = MQTTAsync_sendMessage(client, topic, &pubmsg, &opts);
	if (ret != 0) {
		printf( "mqtt_publish() error, publish result %d\n", ret);
		return -1;
	}

	printf("mqtt_publish(), the payload is %s, the topic is %s \n", payload, topic);
	return opts.token;
}

void subscribe_success(void *context, MQTTAsync_successData *response) {
	printf("subscribe success, the messageId is %d \n", response ? response->token : -1);
}

void subscribe_failure(void *context, MQTTAsync_failureData *response) {
	printf("subscribe failure\n");
	if(response) {
		printf("subscribe_failure(), messageId is %d, code is %d, message is %s\n", response->token, response->code, response->message);
	}
}

int mqtt_subscribe(const char *topic) {

	MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;

	opts.onSuccess = subscribe_success;
	opts.onFailure = subscribe_failure;

	int qos = 1;
	int ret = MQTTAsync_subscribe(client, topic, qos, &opts); //this qos must be 1, otherwise if subscribe failed, the downlink message cannot arrive.

	if (MQTTASYNC_SUCCESS != ret) {
		printf("mqtt_subscribe() error, subscribe failed, ret code %d, topic %s\n", ret, topic);
		return -1;
	}

	printf("mqtt_subscribe(), topic %s, messageId %d\n", topic, opts.token);

	return opts.token;
}

void time_sleep(int ms) {
#if defined(WIN32) || defined(WIN64)
	Sleep(ms);
#else
    usleep(ms * 1000);
#endif
}




// int mqtt_test(void) {

// 	//connect
// 	int ret = mqtt_connect();
// 	if (ret != 0) {
// 		printf("connect failed, result %d\n", ret);
// 	}

// 	time_sleep(2000);

// 	//publish data
// 	char payload [1024];
// 	//char *payload = "{\"services\":[{\"service_id\":\"smokeDetector\",\"properties\":{\"temperature\":ui_temperaturevalue},\"eventTime\":null}]}";
// 	sprintf(payload,
// 		"{\"services\":[{\"service_id\":\"smokeDetector\",\"properties\":{\"temperature\":%.1f},\"eventTime\":null}]}",current_temperature
// 	);
// 	char *report_topic = combine_strings(3, "$oc/devices/", username, "/sys/properties/report");
// 	ret = mqtt_publish(report_topic, payload);
// 	free(report_topic);
// 	report_topic = NULL;

// 	if (ret < 0) {
// 		printf("publish data error, result %d\n", ret);
// 	}
	

// 	return 0;
// }



#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>

// 全局变量保存MQTT连接状态
static int mqtt_connected = 0;

// 初始化MQTT连接（只需要调用一次）
int mqtt_init(void) {
    if (mqtt_connected) {
        return 0; // 已经连接，直接返回
    }
    
    int ret = mqtt_connect();
    if (ret != 0) {
        printf("connect failed, result %d\n", ret);
        mqtt_connected = 0;
        return ret;
    }
    
    mqtt_connected = 1;
    printf("MQTT connected successfully\n");
    return 0;
}

// 发布数据（使用已建立的连接）
int mqtt_publish_data(void) {
    if (!mqtt_connected) {
        printf("MQTT not connected, trying to reconnect...\n");
        if (mqtt_connect() != 0) {
            mqtt_connected = 0;
            return -1;
        }
        mqtt_connected = 1;
    }

    // 发布数据
    char payload[1024];
    sprintf(payload,
        "{\"services\":[{\"service_id\":\"smokeDetector\",\"properties\":{\"temperature\":%.1f},\"eventTime\":null}]}", 
        current_temperature
    );
    
    char *report_topic = combine_strings(3, "$oc/devices/", username, "/sys/properties/report");
    int ret = mqtt_publish(report_topic, payload);
    free(report_topic);
    
    if (ret < 0) {
        printf("publish data error, result %d\n", ret);
        mqtt_connected = 0; // 发布失败，标记为未连接
        return ret;
    }
    
    printf("Data published successfully: %.1f\n", current_temperature);
    return 0;
}

// 检查MQTT连接状态
int mqtt_is_connected(void) {
    return mqtt_connected;
}


























// #include <unistd.h>  
// #include <time.h>    
// #include <stdlib.h>   // 用于free
// #include "../../TEMPERATURE/temperature.h"

// // 定义上报间隔（秒）
// #define UPLOAD_INTERVAL 5

// // 静态变量：仅在当前文件可见，保存MQTT状态
// static int mqtt_connected = 0;       // 连接状态标记
// static time_t last_upload_time = 0;  // 上次上报时间


// // MQTT初始化函数：在main中初始化时调用一次
// void mqtt_init(void) {
//     // 初始化温度模块（仅首次调用时执行）
//     if (!temperature_module_is_initialized()) {
//         adc_display_init();
//         adc_display_start();
//         sleep(1);  // 等待温度模块初始化完成
//     }

//     // 连接MQTT服务器
//     int ret = mqtt_connect();
//     if (ret != 0) {
//         printf("[MQTT] 连接失败，错误码: %d\n", ret);
//         mqtt_connected = 0;
//     } else {
//         printf("[MQTT] 连接成功\n");
//         mqtt_connected = 1;
//         last_upload_time = time(NULL);  // 记录初始时间
//     }
// }

// // MQTT任务处理函数：在main的主循环中调用
// void mqtt_task_handler(void) {
//     if (!mqtt_connected) {
//         static time_t last_reconnect_time = 0;
//         time_t now = time(NULL);
//         if (now - last_reconnect_time >= 5) {
//             printf("[MQTT] 尝试重连...\n");
//             int ret = mqtt_connect();
//             if (ret == 0) {
//                 printf("[MQTT] 重连成功\n");
//                 mqtt_connected = 1;
//                 last_upload_time = now;

//                 // 【关键】重连后重新订阅命令主题
//                 char *cmd_topic = combine_strings(3, "$oc/devices/", username, "/sys/commands/#");
//                 if (cmd_topic) {
//                     mqtt_subscribe(cmd_topic); // 调用订阅函数
//                     free(cmd_topic);
//                 }
//             }
//             last_reconnect_time = now;  // 记录本次重连时间
//         }
//         return;
//     }

//     // 检查是否到达上报时间
//     time_t current_time = time(NULL);
//     if (current_time - last_upload_time >= UPLOAD_INTERVAL) {
//         // 获取当前温度
//         float current_temp = get_current_temperature();
//         printf("[温度] 当前温度: %.1f℃\n", current_temp);

//         // 构建JSON payload
//         char payload[128];
//         snprintf(payload, sizeof(payload),
//                 "{\"services\":[{\"service_id\":\"temperatureMonitor\","
//                 "\"properties\":{\"temperature\":%.1f},"
//                 "\"eventTime\":null}]}",
//                 current_temp);

//         // 拼接上报主题（假设combine_strings已实现）
//         char *report_topic = combine_strings(3, "$oc/devices/", username, "/sys/properties/report");
//         int ret = mqtt_publish(report_topic, payload);
//         free(report_topic);  // 释放动态分配的内存

//         // 处理发布结果
//         if (ret < 0) {
//             printf("[ERROR] 发布失败，错误码: %d\n", ret);
//             mqtt_connected = 0;  // 发布失败时标记为未连接，触发重连
//         } else {
//             printf("[MQTT] 数据上报成功\n");
//             last_upload_time = current_time;  // 更新上次上报时间
//         }
//     }
// }


// // 实现MQTT断开连接函数（使用Paho MQTT库接口）
// void mqtt_disconnect(void) {
//     if (client != NULL) {  // 检查客户端实例是否存在
//         // 断开MQTT连接（超时时间1000ms）
//         int ret = MQTTAsync_disconnect(client, 1000);
//         if (ret != MQTTASYNC_SUCCESS) {
//             printf("[MQTT] 断开连接失败，错误码: %d\n", ret);
//         } else {
//             printf("[MQTT] 连接已断开\n");
//         }

//         // 销毁MQTT客户端实例，释放资源
//         MQTTAsync_destroy(&client);
//         client = NULL;  // 重置客户端指针
//         mqttClientCreateFlag = 0;  // 允许重新创建客户端
//     }
// }

// // （可选）MQTT资源释放函数：程序退出时调用
// void mqtt_cleanup(void) {
//     if (mqtt_connected) {
//         mqtt_disconnect();  // 假设存在mqtt_disconnect函数
//         mqtt_connected = 0;
//         printf("[MQTT] 已断开连接\n");
//     }
// }